#include <asio.hpp>
#include <asio_adapting.h>
#include <google/protobuf/util/delimited_message_util.h>

#include <my_message.pb.h>

#include <chat_message.pb.h>

namespace Test_Protobuf {
    void WriteProtobuf() {
        asio::io_context ioc;
        asio::ip::tcp::acceptor acceptor(ioc, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), 6000));
        for (;;) {
            asio::ip::tcp::socket socket(ioc);
            acceptor.accept(socket);
            net::AsioOutputStream<asio::ip::tcp::socket> aos(socket);
            google::protobuf::io::CopyingOutputStreamAdaptor cos_adp(&aos);
            int i = 0;
            do {
                ++i;
                tutorial::searchRequest my_message;
                my_message.set_query("tell");
                google::protobuf::util::SerializeDelimitedToZeroCopyStream(my_message, &cos_adp);
                cos_adp.Flush();
            } while (true);
        }
    }
}

#include <memory>

class session : public std::enable_shared_from_this<session> {
public:
    session(asio::ip::tcp::socket socket) : _socket(std::move(socket)) {}

    void start() {
        do_read();
    }

private:
    void do_read() {
        auto self(shared_from_this());
        _socket.async_read_some(asio::buffer(_data, MAX_SIZE),[this, self](std::error_code ec, std::size_t size){
            if (!ec) {
                /*std::cout << "Get Msg size = " << size << std::endl;
                chat::chat_message message;
                message.ParseFromString(_data);
                std::cout << message.DebugString() << std::endl;
                memset(_data, 0, MAX_SIZE);*/
                do_write(size);
            }
        });
    }

    void do_write(std::size_t length) {
        auto self(shared_from_this());
        _socket.async_write_some(asio::buffer(_data, length), [this, self](std::error_code ec, std::size_t ){
            if (!ec) {
                do_read();
            }
        });
    }

private:
    asio::ip::tcp::socket _socket;
    enum { MAX_SIZE = 1024 };
    char _data[MAX_SIZE];
};


class server {
public:
    server(asio::io_context& ioc, short port) : _acceptor(ioc, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)) {
        do_accept();
    }
private:
    void do_accept() {
        _acceptor.async_accept([this](std::error_code ec, asio::ip::tcp::socket socket){
            if (!ec) {
                std::cout << "Get Client" << std::endl;
                std::make_shared<session>(std::move(socket)) -> start();
            }
            do_accept();
        });
    }
private:
    asio::ip::tcp::acceptor _acceptor;
};

int main(int argc, char* argv[]) {
    try
    {
        if (argc != 2)
        {
            std::cerr << "Usage: async_tcp_echo_server <port>\n";
            return 1;
        }

        asio::io_context io_context;

        server s(io_context, std::atoi(argv[1]));

        io_context.run();
    }
    catch (std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << "\n";
    }
    return 0;
}